import datetime
import json
import os
import re
import sys
import time
from copy import deepcopy
from collections import Counter

import numpy as np

from feature_set.base_feature import BaseFeature, RequstData

class AppUnRuleV1(BaseFeature):
    def __init__(self):
        super().__init__()
        self.app_name = "app_name"
        self.package_name = "app_package"
        self.inst_time_name = "fi_time"
        self.updt_time_name = "lu_time"
        self.pre_inst_name = "isSystem"
        

        self.time_window_type = ["updt", "inst"]
        self.time_window_dic = {
            "all": sys.maxsize,
            "m6": 180,
            "m3": 90,
            "m1": 30,
        }
        
        self.root_dir = self.get_root_dir(os.path.abspath("."))
        self.conf_dir = os.path.join(self.root_dir, "feature_conf")
        
        self.country_info = None
        self.package_conf = None
        self.feature_dict = None
        
        self.fraud_kw_list = ['auto_generated', 'lineageos.overlay.customization']
        
        self.minhash_redis_host = None
        self.minhash_redis_port = None
        self.minhash_redis_db = None
        self.minhash_basename = None
        
    def get_root_dir(self, path):
        path_list = path.split(os.path.sep)
        index = path_list.index("featurelib")
        return os.path.sep.join(path_list[: index + 1])
        
    def trans_str_to_time(self, str_time):
        return datetime.datetime.strptime(str_time, "%Y-%m-%d %H:%M:%S")
    
    def load_request(self, request_data):
        apply_time = self.trans_str_to_time(request_data.apply_time)
        self.apply_date = apply_time.date()

        try:
            app_list = []
            applist_data = request_data.data_sources["applist_data"]
            app_list = (
                json.loads(applist_data) if type(applist_data) == str else applist_data
            )
        except:
            pass

        if len(app_list) == 0 or type(app_list) != list:
            assert (
                1 == 0
            ), "传入的app_json无法解析或者解析后异常(不是list，或者为空list)"

        name_trans_hash = {
            "app": self.app_name,
            "package": self.package_name,
            "inst_time": self.inst_time_name,
            "updt_time": self.updt_time_name,
            "pre_inst": self.pre_inst_name,
        }

        clean_app_list = []

        time_counts = Counter(item[name_trans_hash['inst_time']] for item in app_list)
        most_inst_time = time_counts.most_common(1)[0][0]
        for app in app_list:
            trans_app = {}
            trans_app["app"] = app[name_trans_hash["app"]]
            trans_app["package"] = app[name_trans_hash["package"]]
            trans_app["inst_time"] = datetime.datetime.utcfromtimestamp(
                int(app[name_trans_hash["inst_time"]]) // 1000
            ) + datetime.timedelta(hours=self.country_info["time_zone"])
            trans_app["inst_date"] = trans_app["inst_time"].date()
            trans_app["updt_time"] = datetime.datetime.utcfromtimestamp(
                int(app[name_trans_hash["updt_time"]]) // 1000
            ) + datetime.timedelta(hours=self.country_info["time_zone"])
            trans_app["updt_date"] = trans_app["updt_time"].date()
            trans_app["up2in_time_diff_days"] = (
                trans_app["updt_date"] - trans_app["inst_date"]
            ).days
            trans_app["up2in_time_diff_hours"] = (
                trans_app["updt_time"] - trans_app["inst_time"]
            ).total_seconds() // 3600
            trans_app["pre_inst"] = app[name_trans_hash["pre_inst"]]
            trans_app["inst_tfdays"] = (self.apply_date - trans_app["inst_date"]).days
            trans_app["updt_tfdays"] = (self.apply_date - trans_app["updt_date"]).days
            if trans_app["inst_time"] == most_inst_time:
                trans_app[name_trans_hash['pre_inst']] = 1
            else:
                trans_app[name_trans_hash['pre_inst']] = 0
            if trans_app["inst_time"] <= apply_time:
                clean_app_list.append(deepcopy(trans_app))

        self.app_list = clean_app_list
        
    def gen_features(self, data: RequstData):
        res_map = {}
        try:
            self.load_request(data)
        except AssertionError as e:
            for k, v in self.feature_dict.items():
                if v == 'str':
                    res_map[k] = ''
                else:
                    res_map[k] = -999
            return res_map

        res_map = self.get_cnt_feature(res_map)
        res_map = self.get_package_install_feature(res_map)
        res_map = self.get_fraud_kw_feature(res_map)
        res_map = self.get_fingerprint_feature(res_map, data.app_user_id)
        print(res_map)
        return res_map
    
    def _init_package_install_feature(self, res_map):
        for package_name in self.package_conf:
            res_map[f'{package_name}_install'] = 0
        return res_map
    
    def get_package_install_feature(self, res_map):
        res_map = self._init_package_install_feature(res_map)
        for app in self.app_list:
            for package_name in self.package_conf:
                if app['package'] == self.package_conf[package_name]:
                    res_map[f'{package_name}_install'] = 1
        return res_map
    
    def _init_fraud_kw_feature(self, res_map):
        for fraud_kw in self.fraud_kw_list:
            fraud_kw = fraud_kw.replace('.', '_')
            res_map[f'{fraud_kw}_install'] = 0
        return res_map
    
    def get_fraud_kw_feature(self, res_map):
        res_map = self._init_fraud_kw_feature(res_map)
        for app in self.app_list:
            for fraud_kw in self.fraud_kw_list:
                if fraud_kw in app['package'].lower() or fraud_kw in app['app'].lower():
                    fraud_kw = fraud_kw.replace('.', '_')
                    res_map[f'{fraud_kw}_install'] = 1
        return res_map
    
    def _init_cnt_feature(self, res_map):
        res_map['app_cnt'] = 0
        res_map['pre_inst_cnt'] = 0
        res_map['non_pre_inst_cnt'] = 0
        return res_map
    
    def get_cnt_feature(self, res_map):
        res_map = self._init_cnt_feature(res_map)
        res_map['app_cnt'] = len(self.app_list)
        res_map['pre_inst_cnt'] = len([app for app in self.app_list if app['pre_inst'] == 1])
        res_map['non_pre_inst_cnt'] = res_map['app_cnt'] - res_map['pre_inst_cnt']
        return res_map

    def _init_fingerprint_feature(self, res_map):
        res_map['fingerprint_identical_user_cnt'] = 0
        for i in range(3):
            res_map[f'fingerprint_top{i+1}_similarity'] = 0
            res_map[f'fingerprint_top{i+1}_sim_uid'] = ''
        for threshold in [0.7, 0.8, 0.9, 0.95, 0.97, 0.99]: 
            res_map[f'fingerprint_similar_user_cnt_{int(threshold*100)}'] = 0
        return res_map

    def get_fingerprint_feature(self, res_map, user_id):
        res_map = self._init_fingerprint_feature(res_map)
        app_list = [app['package'] for app in self.app_list if app['pre_inst'] == 0]
        user_similarities = self.minhash_redis.find_and_add(user_id, app_list, 0, False)
        user_similarities = [user_similarity for user_similarity in user_similarities if user_similarity['minhash_key'] != f'minhash:{self.minhash_basename}:{user_id}']
        if len(user_similarities) > 0:
            identical_users = [u for u in user_similarities if u['similarity'] == 1]
            res_map['fingerprint_identical_user_cnt'] = len(identical_users)
            for i in range(3):
                if i < len(user_similarities):
                    res_map[f'fingerprint_top{i+1}_similarity'] = user_similarities[i]['similarity']
                    sim_minhash_key = user_similarities[i]['minhash_key']
                    sim_user_id = sim_minhash_key.split(':')[-1]
                    res_map[f'fingerprint_top{i+1}_sim_uid'] = sim_user_id
            for threshold in [0.7, 0.8, 0.9, 0.95, 0.97, 0.99]:
                similar_users = [u for u in user_similarities if u['similarity'] >= threshold]
                res_map[f'fingerprint_similar_user_cnt_{int(threshold*100)}'] = len(similar_users)
        return res_map